package com.eedi.framework.oauth2.service;

import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.enums.CommonStatusEnum;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.common.util.monitor.TracerUtils;
import com.eedi.framework.common.util.servlet.ServletUtils;
import com.eedi.framework.common.util.validation.ValidationUtils;
import com.eedi.framework.logger.controller.sys.vo.loginlog.SysLoginLogCreateReq;
import com.eedi.framework.logger.controller.sys.vo.loginlog.SysLogoutLogCreateReq;
import com.eedi.framework.logger.enmus.SysLoginLogTypeEnum;
import com.eedi.framework.logger.enmus.SysLoginResultEnum;
import com.eedi.framework.logger.service.SysLoginLogService;
import com.eedi.framework.oauth2.dal.dataobject.SysOAuth2AccessTokenDO;
import com.eedi.framework.oauth2.controller.sys.vo.auth.SysAuthLoginReq;
import com.eedi.framework.oauth2.controller.sys.vo.auth.SysAuthLoginResp;
import com.eedi.framework.oauth2.convert.SysAuthConvert;
import com.eedi.framework.permission.service.SysRoleService;
import com.eedi.framework.user.controller.sys.vo.SysUserAccountPageReq;
import com.eedi.framework.user.controller.sys.vo.SysUserLoginInfoResp;
import com.eedi.framework.user.service.SysUserService;
import com.xingyuv.captcha.model.common.ResponseModel;
import com.xingyuv.captcha.model.vo.CaptchaVO;
import com.xingyuv.captcha.service.CaptchaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.List;
import java.util.Objects;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.servlet.ServletUtils.getClientIP;
import static com.eedi.framework.oauth2.enums.SysOauth2ErrorCodeConstants.*;

/**
 * Auth Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class SysAuthServiceImpl implements SysAuthService {

    //    @Resource
//    private AdminUserService userService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysLoginLogService loginLogService;
    @Resource
    private SysOAuth2TokenService sysOAuth2TokenService;
    //    @Resource
//    private SocialUserService socialUserService;

    @Resource
    private Validator validator;
    @Resource
    private CaptchaService captchaService;
    @Resource
    private SysRoleService sysRoleService;

//    @Resource
//    private SmsCodeApi smsCodeApi;

    /**
     * 验证码的开关，默认为 true
     */
    @Value("${eedi.captcha.enable:true}")
    private Boolean captchaEnable;

    @Override
    public SysUserLoginInfoResp authenticate(String sysUserLoginId, String password) {
        final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
        // 校验账号是否存在
        SysUserLoginInfoResp user = sysUserService.getLoginInfoByLoginId(sysUserLoginId);
        if (user == null) {
            createLoginLog(null, sysUserLoginId, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
            throw exception(SYS_AUTH_LOGIN_BAD_CREDENTIALS);
        }
        if (!sysUserService.isPasswordMatch(sysUserLoginId, password)) {
            createLoginLog(user.getSysUserId(), sysUserLoginId, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
            throw exception(SYS_AUTH_LOGIN_BAD_CREDENTIALS);
        }
        // 校验是否禁用
        if (CommonStatusEnum.DISABLE.equals(user.getSysUserAccountStatus())) {
            createLoginLog(user.getSysUserId(), sysUserLoginId, logTypeEnum, SysLoginResultEnum.USER_DISABLED);
            throw exception(SYS_AUTH_LOGIN_USER_DISABLED);
        }
        return user;
    }

    @Override
    public SysAuthLoginResp login(SysAuthLoginReq reqVO) {
        // 校验验证码
        validateCaptcha(reqVO);

        // 使用账号密码，进行登录
        SysUserLoginInfoResp user = authenticate(reqVO.getSysUserLoginId(), reqVO.getPassword());

        // 如果 socialType 非空，说明需要绑定社交用户  todo 1.0.0中没有社交或第三方登录功能
//        if (reqVO.getSocialType() != null) {
//            socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
//                    reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
//        }
        // 创建 Token 令牌，记录登录日志
        return createTokenAfterLoginSuccess(user.getSysUserId(), reqVO.getSysUserLoginId(), SysLoginLogTypeEnum.LOGIN_USERNAME, reqVO.getSysClientId());
    }

//    @Override
//    public void sendSmsCode(AuthSmsSendReqVO reqVO) {
//        // 登录场景，验证是否存在
//        if (userService.getUserByMobile(reqVO.getMobile()) == null) {
//            throw exception(AUTH_MOBILE_NOT_EXISTS);
//        }
//        // 发送验证码
//        smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
//    }
//
//    @Override
//    public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) {
//        // 校验验证码
//        smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SysSmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP()));
//
//        // 获得用户信息
//        AdminUserDO user = userService.getUserByMobile(reqVO.getMobile());
//        if (user == null) {
//            throw exception(USER_NOT_EXISTS);
//        }
//
//        // 创建 Token 令牌，记录登录日志
//        return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
//    }

    private void createLoginLog(String userId, String sysUserLoginId,
                                SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) {
        // 插入登录日志
        SysLoginLogCreateReq reqDTO = new SysLoginLogCreateReq();
        reqDTO.setSysLogType(logTypeEnum);
        reqDTO.setSysTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(userId);
        reqDTO.setUserType(getUserType());
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(getClientIP());
        reqDTO.setSysUserLoginId(sysUserLoginId);
        reqDTO.setResult(loginResult);
        loginLogService.createLoginLog(reqDTO);
        // 更新最后登录时间
        if (userId != null && Objects.equals(SysLoginResultEnum.SUCCESS, loginResult)) {
            sysUserService.updateUserLogin(userId, getClientIP());
        }
    }

//    @Override
//    public SysAuthLoginResp socialLogin(SysAuthSocialLoginReq reqVO) {
//        // 使用 code 授权码，进行登录。然后，获得到绑定的用户编号
//        SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(SysUserTypeEnum.ADMIN.getValue(), reqVO.getType(),
//                reqVO.getCode(), reqVO.getState());
//        if (socialUser == null || socialUser.getUserId() == null) {
//            throw exception(SYS_AUTH_THIRD_LOGIN_NOT_BIND);
//        }
//
//        // 获得用户
//        AdminUserDO user = userService.getUser(socialUser.getUserId());
//        if (user == null) {
//            throw exception(USER_NOT_EXISTS);
//        }
//
//        // 创建 Token 令牌，记录登录日志
//        return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
//    }

    @VisibleForTesting
    void validateCaptcha(SysAuthLoginReq reqVO) {
        // 如果验证码关闭，则不进行校验
        if (!captchaEnable) {
            return;
        }
        // 校验验证码
        ValidationUtils.validate(validator, reqVO, SysAuthLoginReq.CodeEnableGroup.class);
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
        ResponseModel response = captchaService.verification(captchaVO);
        // 验证不通过
        if (!response.isSuccess()) {
            // 创建登录失败日志（验证码不正确)
            createLoginLog(null, reqVO.getSysUserLoginId(), SysLoginLogTypeEnum.LOGIN_USERNAME, SysLoginResultEnum.CAPTCHA_CODE_ERROR);
            throw exception(SYS_AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
        }
    }

    private SysAuthLoginResp createTokenAfterLoginSuccess(String sysUserId, String sysUserLoginId, SysLoginLogTypeEnum logType, String sysClientId) {
        // 插入登陆日志
        createLoginLog(sysUserId, sysUserLoginId, logType, SysLoginResultEnum.SUCCESS);
        // 创建访问令牌
        SysOAuth2AccessTokenDO accessTokenDO = sysOAuth2TokenService.createAccessToken(sysUserId, getUserType(),
                sysClientId, null);
        // 构建返回结果
        return SysAuthConvert.INSTANCE.convert(accessTokenDO);
    }

    @Override
    public SysAuthLoginResp refreshToken(String refreshToken, String sysClientId) {
        SysOAuth2AccessTokenDO accessTokenDO = sysOAuth2TokenService.refreshAccessToken(refreshToken, sysClientId);
        return SysAuthConvert.INSTANCE.convert(accessTokenDO);
    }

    @Override
    public Long countUser() {
        return sysUserService.countUser();
    }

    @Override
    public PageResult<SysUserLoginInfoResp> query(SysUserAccountPageReq req) {
        List<String> sysUserIds = sysRoleService.getSysUserIdByDefaultSysRoleCode();
        return sysUserService.page(req, sysUserIds);
    }

    @Override
    public void logout(String token, SysLoginLogTypeEnum logType) {
        // 删除访问令牌
        SysOAuth2AccessTokenDO accessTokenDO = sysOAuth2TokenService.removeAccessToken(token);
        if (accessTokenDO == null) {
            return;
        }
        // 删除成功，则记录登出日志
        createLogoutLog(accessTokenDO.getSysUserId(), accessTokenDO.getSysUserType(), logType);
    }

    private void createLogoutLog(String sysUserId, UserTypeEnum userType, SysLoginLogTypeEnum logType) {
        SysLogoutLogCreateReq reqDTO = new SysLogoutLogCreateReq();
        reqDTO.setSysLogType(logType);
        reqDTO.setSysTraceId(TracerUtils.getTraceId());
        reqDTO.setUserId(sysUserId);
        reqDTO.setUserType(userType);
        reqDTO.setUserAgent(ServletUtils.getUserAgent());
        reqDTO.setUserIp(getClientIP());
        reqDTO.setResult(SysLoginResultEnum.SUCCESS);
        loginLogService.createLogoutLog(reqDTO);
    }

    private String getUsername(String userId) {
        if (userId == null) {
            return null;
        }
        SysUserLoginInfoResp sysUserLoginInfoResp = sysUserService.getLoginInfoByLoginId(userId);
        return sysUserLoginInfoResp != null ? sysUserLoginInfoResp.getSysUserLoginId() : null;
    }

    private UserTypeEnum getUserType() {
        return UserTypeEnum.MEMBER_SYS;
    }

}
